﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace XNAGraffiti
{
    public enum CrazyPaintMode
    {
        DrawLineFade,
        DrawGlowy,
        DrawSolid
    }
    public class CrazyPaint : Graffiti
    {
        private Game m_game;
        SpriteBatch spriteBatch;
        BloomComponent bloom;
        KeyboardState oldState;
        private Point[] GetPoints(int x0, int x1, int y0, int y1)
        {
            var points = new List<Point>();

            int dy = y1 - y0;
            int dx = x1 - x0;
            int stepx, stepy;

            if (dy < 0) { dy = -dy; stepy = -1; } else { stepy = 1; }
            if (dx < 0) { dx = -dx; stepx = -1; } else { stepx = 1; }
            dy <<= 1;                                                  // dy is now 2*dy
            dx <<= 1;

            points.Add(new Point(x0, y0));

            if (dx > dy)
            {
                int fraction = dy - (dx >> 1);
                while (x0 != x1)
                {
                    if (fraction >= 0)
                    {
                        y0 += stepy;
                        fraction -= dx;
                    }
                    x0 += stepx;
                    fraction += dy;
                    points.Add(new Point(x0, y0));
                }
            }
            else
            {
                int fraction = dx - (dy >> 1);
                while (y0 != y1)
                {
                    if (fraction >= 0)
                    {
                        x0 += stepx;
                        fraction -= dy;
                    }
                    y0 += stepy;
                    fraction += dx;
                    points.Add(new Point(x0, y0));
                }
            }

            return points.ToArray();
        }
        private Texture2D m_spriteTexture;
        private RenderTarget2D m_renderTarget;
        private RenderTarget2D m_renderTargetGlow;

        private Color m_currentColor = Color.Blue;
        private double counter;
        
        public void CycleColor()
        {
           counter += 0.002;

            if(counter >= 1)
                counter = 0;

            m_currentColor = WheelColor(counter);
            
        }

        private const double ONE_SIXTH =
    0.16666666666666666666666666666667;
        private const double ONE_THIRD =
            0.33333333333333333333333333333333;
        private const double TWO_THIRDS =
            0.66666666666666666666666666666667;
        private const double FIVE_SIXTHS =
            0.83333333333333333333333333333333;
        public Color WheelColor(double d)
        {
            if ((d < 0.0) || (d > 1.0))
            {
                throw new ArgumentOutOfRangeException("d",
                    d, "d must be between 0.0 and 1.0, inclusive");
            }
            double R = 1;
            double G = 1;
            double B = 1;
            if (d < ONE_SIXTH)
            {
                G = d / ONE_SIXTH;
                B = 0;
            }
            else if (d < ONE_THIRD)
            {
                R = 1 - ((d - ONE_SIXTH) / ONE_SIXTH);
                B = 0;
            }
            else if (d < 0.5)
            {
                R = 0;
                B = (d - ONE_THIRD) / ONE_SIXTH;
            }
            else if (d < TWO_THIRDS)
            {
                R = 0;
                G = 1 - ((d - 0.5) / ONE_SIXTH);
            }
            else if (d < FIVE_SIXTHS)
            {
                R = (d - TWO_THIRDS) / ONE_SIXTH;
                G = 0;
            }
            else
            {
                B = 1 - ((d - FIVE_SIXTHS) / ONE_SIXTH);
                G = 0;
            }
            return new Color((byte)(R * 255),
                (byte)(G * 255), (byte)(B * 255));
        }

        public CrazyPaint(Game game) : base(game)
        {
            m_game = game;
            bloom = new BloomComponent(game);
            oldState = Keyboard.GetState();
        }

        public void LoadContent()
        {

            spriteBatch = new SpriteBatch(m_game.GraphicsDevice);
            m_spriteTexture = m_game.Content.Load<Texture2D>("paint");
            bloom.Initialize();
            bloom.ShowBuffer = BloomComponent.IntermediateBuffer.BlurredBothWays;
            InitializeRenderTargets();
        }

        private void DisposeRenderTargets()
        {
            m_renderTarget.Dispose();
            m_renderTargetGlow.Dispose();
        }

        private CrazyPaintMode m_paintMode = CrazyPaintMode.DrawLineFade;
        public CrazyPaintMode PaintMode
        {
            get { return m_paintMode; }
            set
            {
                m_paintMode = value;
            }
        }

        private bool m_autoCycleColors = true;

        public bool AutoCycleColors
        {
            get { return m_autoCycleColors; }
            set
            {
                m_autoCycleColors = value;
            }
        }

        public int BrushSize
        {
            get { return m_brushSize; }
            set
            {
                if (value < 5)
                    return;

                if (value > 250)
                    return;

                m_brushSize = value;
            }
        }

        private void InitializeRenderTargets()
        {
            PresentationParameters pp = m_game.GraphicsDevice.PresentationParameters;

            int width = pp.BackBufferWidth;
            int height = pp.BackBufferHeight;
            SurfaceFormat format = pp.BackBufferFormat;
            m_renderTarget = new RenderTarget2D(m_game.GraphicsDevice, width, height, 1, SurfaceFormat.Color, RenderTargetUsage.PreserveContents);
            m_renderTargetGlow = new RenderTarget2D(m_game.GraphicsDevice, width, height, 1, SurfaceFormat.Color, RenderTargetUsage.PreserveContents);

            m_game.GraphicsDevice.SetRenderTarget(0, m_renderTarget);
            m_game.GraphicsDevice.Clear(ClearOptions.Target, Color.TransparentBlack, 1.0f, 0);
            m_game.GraphicsDevice.SetRenderTarget(0, null);
        }

        public void Update(GameTime gameTime)
        {
            if (m_lastGameTime == null)
                m_lastGameTime = gameTime;

            KeyboardState newState = Keyboard.GetState();

            if (newState.IsKeyDown(Keys.C) || AutoCycleColors)
                CycleColor();

            if (newState.IsKeyDown(Keys.A))
            {
                
            }
            else if (oldState.IsKeyDown(Keys.A))
            {
                AutoCycleColors = !AutoCycleColors;
            }

            if (newState.IsKeyDown(Keys.Up))
            {
                
            }
            else if (oldState.IsKeyDown(Keys.Up))
            {
                BrushSize+=5;
            }

            if (newState.IsKeyDown(Keys.Down))
            {
                
            }
            else if (oldState.IsKeyDown(Keys.Down))
            {
                BrushSize -= 5;
            }

            if (newState.IsKeyDown(Keys.E))
            {

            }
            else if (oldState.IsKeyDown(Keys.E))
            {
                Clear();
                switch (PaintMode)
                {
                    case CrazyPaintMode.DrawLineFade:
                        PaintMode = CrazyPaintMode.DrawGlowy;
                        break;
                    case CrazyPaintMode.DrawGlowy:
                        PaintMode = CrazyPaintMode.DrawSolid;
                        break;
                    case CrazyPaintMode.DrawSolid:
                        PaintMode = CrazyPaintMode.DrawLineFade;
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                }
            }

            oldState = newState;
            m_lastGameTime = gameTime;
            bloom.Update(gameTime);
        }

        private GameTime m_lastGameTime;

        public override void Draw(GameTime gameTime)
        {
            m_lastGameTime = gameTime;
        }

        private Point? m_lastPoint = new Point();
        private Point? m_lastEndPoint = new Point();

        private void Clear()
        {
            m_game.GraphicsDevice.Clear(ClearOptions.Target, Color.TransparentBlack, 0f, 0);
        }

        private void CheckClearZone(Point point)
        {
            Vector2 p = new Vector2(point.X,point.Y);
            Vector2 p2 = new Vector2(m_renderTarget.Width, m_renderTarget.Height);

            var diff = p - p2;

            if (diff.Length() < 100)
                Clear();
        }

        private int m_brushSize = 25;

        public override void DrawLine(Point? startPoint, Point? endPoint)
        {
            if (!startPoint.HasValue || !endPoint.HasValue)
            {
                return;
            }


            PresentationParameters pp = m_game.GraphicsDevice.PresentationParameters;

            int bufferWidth = pp.BackBufferWidth;
            int bufferHeight = pp.BackBufferHeight;

            if (m_renderTarget.Width != bufferWidth || m_renderTarget.Height != bufferHeight)
            {
                DisposeRenderTargets();
                InitializeRenderTargets();
            }

            var currentVector = new Vector2(startPoint.Value.X, startPoint.Value.Y);
            var endVector = new Vector2(endPoint.Value.X, endPoint.Value.Y);
            var lastCurrentDiff = currentVector - endVector;

            m_game.GraphicsDevice.SetRenderTarget(0, m_renderTarget);

            if (Keyboard.GetState().IsKeyDown(Keys.Space))
                Clear();

            var currentPoint = new Point((int)currentVector.X, (int)currentVector.Y);
            var lastPoint = new Point((int)endVector.X, (int)endVector.Y);

            var points = GetPoints(currentPoint.X, lastPoint.X, currentPoint.Y, lastPoint.Y);

            spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.None);

            if (PaintMode != CrazyPaintMode.DrawGlowy)
            if(m_lastPoint != startPoint && m_lastEndPoint != endPoint)
            for (int i =0; i < points.Length; i++)
            {
                var point = points[i];

                float len = lastCurrentDiff.Length();



                var destRectangle = new Rectangle(point.X - (BrushSize/2), point.Y - (BrushSize/2), (int)BrushSize, (int)BrushSize);
                spriteBatch.Draw(m_spriteTexture, destRectangle, null, m_currentColor, 0,Vector2.Zero, SpriteEffects.None, (float)((float)i * 0.001f));
                CheckClearZone(point);
            }
            spriteBatch.End();
            
            if(PaintMode == CrazyPaintMode.DrawLineFade)
            {
                bloom.Settings = new BloomSettings("Blurry", 0.001f, 0.01f, 1, 0.05f, 1, 1);

                bloom.Draw(m_lastGameTime);
            }
                

            bloom.Settings = new BloomSettings("Soft", 0.003f, 4, 1, 1, 1, 1);

            m_game.GraphicsDevice.SetRenderTarget(0, m_renderTargetGlow);
            spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate,
                             SaveStateMode.SaveState);
            if (m_lastPoint != startPoint)
            foreach (var point in points)
            {
                float len = lastCurrentDiff.Length();

                var destRectangle = new Rectangle(point.X - ((BrushSize + 10) / 2), point.Y - ((BrushSize + 10) / 2), (int)BrushSize + 10, (int)BrushSize + 10);
                spriteBatch.Draw(m_spriteTexture, destRectangle, m_currentColor);
            }

            spriteBatch.End();

            bloom.Draw(m_lastGameTime);

            m_game.GraphicsDevice.SetRenderTarget(0, null);

            var texture = m_renderTarget.GetTexture();
            var texture2 = m_renderTargetGlow.GetTexture();

            spriteBatch.Begin(SpriteBlendMode.AlphaBlend,
                              SpriteSortMode.Immediate,
                              SaveStateMode.SaveState);
            if (PaintMode != CrazyPaintMode.DrawGlowy)
            spriteBatch.Draw(texture, new Rectangle(0, 0, texture.Width, texture.Height), Color.White);
            spriteBatch.Draw(texture2, new Rectangle(0, 0, texture.Width, texture.Height), Color.White);
            spriteBatch.End();
            bloom.Settings = new BloomSettings("Soft", 0, 3, 1, 1, 1, 1);
            bloom.ShowBuffer = BloomComponent.IntermediateBuffer.FinalResult;
            bloom.Draw(m_lastGameTime);
            bloom.ShowBuffer = BloomComponent.IntermediateBuffer.BlurredBothWays;

            m_lastPoint = startPoint;
            m_lastEndPoint = endPoint;
        }
    }
}
